
from bf_parseline import *
from bf_parseline2 import *



class DocMach:
    def __init__(self):    # 不用处理错误，直接中断+err置1
        self.rules = []

    def reg(self, state, a):
        def g(f):
            self.rules.append(((state, a), f))
        return g

    def run(self, data, debug=False):
        state = Ss()
        data += ["end"]
        for a in data:
            state = self.reduction(state, a, debug)

        if isinstance(state, Ss):
            return listb()          # line用元组，函数调用用列表，block用listb
        elif isinstance(state, Xs):
            return listb(state.l + [state.x])
        else:
            raise Exception()


    def reduction(self, state, a, debug):
        for (t1, t2), f in self.rules:
            if match(state, t1) and match(a, t2):
                if debug:
                    print("match from", state, a)
                    # ~ print( "match", repr2(t1), repr2(t2))
                    print( "match", t1, t2)
                r = f(state, a)
                if debug:
                    print("to", r)
                return r
            else:
                pass
                #print('nm', t1, t2)
        else:
            print("err1", state, a)
            raise Exception("无可匹配的规则")


class State:
    def __repr__(self):
        return f"<{type(self).__name__}>"


class Ss(State):
    def __init__(self):
        pass
    def add1(self, x):
        return Xs([], x)

class Xs(State):
    def __init__(self, l, x):
        self.l = l
        self.x = x

    def add(self, x):
        return Xs(self.l+[self.x], x)

    def pop(self):
        if len(self.l) ==0:
            return Ss(), self.x
        else:
            return Xs(self.l[:-1], self.l[-1]), self.x


class Fs(State):
    def __init__(self, last, f, k):
        assert not isinstance(last, Fxs)
        self.last = last
        self.f = f
        self.k = k      # 左右结合权重

    def eat(self, x):
        return Fxs(self.last, self.f, x, self.k)

class Fxs(State):
    def __init__(self, last, f, x, k):
        assert not isinstance(last, Fxs)
        self.last = last

        self.f = f
        self.x = x
        self.k = k      # 左右结合权重


    def spew(self):
        return Fs(self.last, self.f, self.k), self.x

    def end(self):
        if isinstance(self.last, Ss):
            return self.last.add1(self.f + [self.x])
        elif isinstance(self.last, Xs):
            return self.last.add(self.f + [self.x])
        elif isinstance(self.last, Fs):
            return self.last.eat(self.f + [self.x])
        else:
            raise Exception('Fxs bad end')


def repr2(pattern):
    if isinstance(pattern, type):
        return pattern.__name__
    elif isinstance(pattern, list):
        s = ', '.join(repr2(x) for x in pattern)
        return "[" + s + "]"
    elif callable(pattern):
        return pattern.__name__
    else:
        return pattern.__repr__()

def repr3(tree):
    if isinstance(tree, list):
        s = ', '.join(repr3(x) for x in tree)
        return "[" + s + "]"
    elif isinstance(tree, str):
        return tree
    else:
        #print(type(tree))
        return repr(tree)


def match(x, pattern):
    if isinstance(pattern, type):
        return isinstance(x, pattern)
    elif isinstance(pattern, list):
        flag = False
        for p2 in pattern:
            if match(x, p2):
                flag = True
                break
        return flag
    elif callable(pattern):
        return pattern(x)
    else:
        return x == pattern



dm = DocMach()

table_head = {}
table_op = {}

class Head:
    def __init__(self, f, k):
        self.f = f
        self.k = k

class Op:
    def __init__(self, f, k):
        self.f = f
        self.k = k

def new_head(f, k):
    table_head[f] = Head(f, k)

def HEAD(x):
    #raise NotImplementedError()
    if not isinstance(x[0], Pat):
        return False
    return x[0].name in table_head

def new_op(f, k):
    table_op[f] = Op(f, k)

def OP2(x):
    if not isinstance(x[0], Pat):
        return False
    return x[0].name in table_op

def LINE(x):
    if not isinstance(x[0], Pat):
        return False
    return x[0].name in ['Lassign', 'Oexp1', 'Oexp2', 'Onum', 'Oword']

def BLOCK(x):
    #raise NotImplementedError()
    if isinstance(x, list):
        return True
    else:
        return False


new_head('Lif', 5)
new_op('Lelif', 5)
new_op('Lelse', 5)

new_head('Lfor', 5)

@dm.reg(Ss, HEAD)
def f(s, a):
    f = a[0].name
    return Fs(s, [a], table_head[f].k)

@dm.reg(Ss, LINE)
def f(s, a):
    return s.add1(a)

@dm.reg(Xs, LINE)
def f(s, a):
    return s.add(a)

@dm.reg(Xs, HEAD)
def f(s, a):
    f = a[0].name
    return Fs(s, [a], table_head[f].k)

@dm.reg(Fs, BLOCK)
def f(fs, a):
    # ~ print("start", fs.f)
    return fs.eat(dm.run(a))


@dm.reg(Fxs, LINE)
def f(fxs, a):
    s = fxs.end()
    if isinstance(s, Xs):
        return s.add(a)
    else:
        raise Exception('e16')



@dm.reg(Fxs, OP2)
def f(fxs, a):
    f = a[0].name
    opk = table_op[f].k
    if fxs.k >= opk:
        while True:
            s2 = fxs.end()
            if isinstance(s2, Xs):
                s3, b = s2.pop()
                return Fs(s3, [a, b], opk)
            else:
                raise NotImpletedError()

                '''
                fxs.last is not None and fxs.last.k >= opk:
                fs = fxs.last
                fxs = Fxs(fs.last, fs.f, fxs.f + [fxs.x], fs.k)
                return Fs(fxs.last, [a, fxs.f + [fxs.x]], opk)
                '''
    else:
        loser = fxs.spew()
        return Fs(loser, [f, fxs.x], opk)



@dm.reg(Fs, HEAD)
def f(fs, a):
    return Fs(fs, [a], table_head[a].k)


@dm.reg(Fxs, "end")
def f(fxs, a):
    while True:
        s2 = fxs.end()
        if isinstance(s2, Xs):
            return s2

@dm.reg(Xs, "end")
def f(s, a):
    return s

@dm.reg(Ss, "end")
def f(s, a):
    return s


#####

def parsedoc_step1(docl: "bf_lex.Doc.l"):
    assert isinstance(docl, list)
    def do(lb):
        if isinstance(lb, Line):
            t1 = parseline(lb.l)
            return t1
        elif isinstance(lb, Block):
            return [do(x) for x in lb.l]
    l = [do(lb) for lb in docl]
    return dm.run(l)

'''
def showd(x):
    from tool_treeprint import tree
    def name(x):
        if isinstance(x, tuple):
            return str(x[0])
        elif isinstance(x, list):
            return '(block)'
        else:
            return str(x)
    def member(x):
        if isinstance(x, list):
            return x
        else:
            return []
    tree(x, name, member)

'''

def showd2(x):
    from tool_treeprint import tree
    def name(x):
        if isinstance(x, tuple):
            return '<line>'
        elif isinstance(x, listb):
            return '<block>'
        elif isinstance(x, list):
            return str(x[0][0])
        else:
            0/0
            return str(x)
    def member(x):
        if isinstance(x, tuple):
            return []
        elif isinstance(x, listb):
            return x
        elif isinstance(x, list):
            return x[1:]
        else:
            0/0
            return str(x)
    tree(x, name, member)



def test(s):
    doc = scan(s)
    show(doc)
    r = parsedoc_step1(doc.l)
    print(r)
    showd2(r)




# todo: lex把冒号单独处理
# 显示单行已经非常困难
if __name__ == "__main__":
    #("for i in range(5).reversed() if i % 3")
    code = """
a = input()
if a < 1:
    x.l[0] = 1
    l[0].x = 1
    3
elif a < 2:
    (x, y) = (y, x)
    4
else:
    20
print(5)
for i in range(3):
  for j in range(3):
    print(i * j) / 2

input()
    """
    code = """
a = input()
if a < 1:
    x.l[0] = 1
    l[0].x = 1
    3
2
    """

    test(code)